﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using DP.RecallDeal.Interface.ObjectTransformer;

namespace DP.RecallDeal.Interface.ObjectTransformer
{
    public class ObjectTransformerTargetFieldService : ObjectTranformerService, IObjectTranformerService
    {
        //public override object GetValue(object instance, string fieldsChain)
        //{
        //    if (instance == null && string.IsNullOrEmpty(fieldsChain))
        //        return null;

        //    string[] fields = fieldsChain.Split('.');
        //    string field = fields[0];

        //    try
        //    {
        //        FieldInfo fieldInfo = instance.GetType().GetField(field);
        //        object fieldInstance = fieldInfo.GetValue(instance);

        //        if (fields.Length > 1) //It has child field
        //        {
        //            //Remove first element in fields chain
        //            fields = fields.Where((x, idx) => idx != 0).ToArray();

        //            //Form left child fields into format Parent.Child.GrandChild
        //            string leftfields = string.Join(".", fields);

        //            //Recursive call
        //            return GetValue(fieldInstance, leftfields);
        //        }
        //        else //No more child field
        //            return fieldInstance;
        //    }
        //    catch (Exception ex)
        //    {
        //        MappingExceptions.Add(new MappingException
        //        {
        //            Message = "Mapping exception occurs while gets value from source field: " + field,
        //            InnerException = ex
        //        });
        //    }
        //    return null;    
        //}

        public override void FillObject(string fieldsChain, object targetInstance, object sourceValue)
        {
            if (targetInstance == null || sourceValue == null || string.IsNullOrEmpty(fieldsChain))
                return;

            string[] fields = fieldsChain.Split('.');
            string field = fields[0];

            try
            {

                FieldInfo fieldInfo = targetInstance.GetType().GetField(field);

                if (fields.Length > 1) //It has child field
                {
                    object fieldInstance = fieldInfo.GetValue(targetInstance);

                    if (fieldInstance == null)
                    {
                        fieldInstance = Activator.CreateInstance(fieldInfo.FieldType);
                        CastAndSetValue(fieldInfo, targetInstance, fieldInstance);
                    }

                    //Remove first element in fields chain
                    fields = fields.Where((x, idx) => idx != 0).ToArray();

                    //Form left child fields into format Parent.Child.GrandChild
                    string leftfields = string.Join(".", fields);

                    //Recursive call
                    FillObject(leftfields, fieldInstance, sourceValue);
                }
                else //No more child field
                    CastAndSetValue(fieldInfo, targetInstance, sourceValue);
            }
            catch (InvalidCastException castException)
            {
                MappingExceptions.Add(new MappingException
                {
                    Message = "Mapping exception occurs while sets value to target field: " + field,
                    InnerException = castException
                });
            }
            catch (ArgumentException argumentException)
            {
                MappingExceptions.Add(new MappingException
                {
                    Message = "Mapping exception occurs while sets value to target field: " + field,
                    InnerException = argumentException
                });
            }
            catch (NullReferenceException nullException)
            {
                MappingExceptions.Add(new MappingException
                {
                    Message = "Mapping exception occurs while sets value to target field: " + field,
                    InnerException = nullException
                });
            }
            catch (Exception ex)
            {
                MappingExceptions.Add(new MappingException
                {
                    Message = "Mapping exception occurs while sets value to target field: " + field,
                    InnerException = ex
                });
            }
        }

        protected void CastAndSetValue(FieldInfo info, object instance, object value)
        {
            var destType = info.FieldType.IsNullableType() ? Nullable.GetUnderlyingType(info.FieldType) : info.FieldType;
            var convertedValue = Convert.ChangeType(value, destType);

            info.SetValue(instance, convertedValue);
        }
    }
}
